home *** CD-ROM | disk | FTP | other *** search
/ Power Programmierung / Power-Programmierung (Tewi)(1994).iso / magazine / drdobbs / 1987 / 08 / kranzlst.aug < prev    next >
Text File  |  1987-08-12  |  24KB  |  879 lines

  1. /*------------------------------------------------------------------------------
  2. --------------------------------------------------------------------------------
  3. --            Name: DIFF.C
  4. --       Processor: VAX | MS-DOS
  5. --           Class: C Program
  6. --   Creation Date: 1/8/87
  7. --        Revision:
  8. --          Author: D. Krantz
  9. --
  10. --     Description: File compare and change-bar for text files.
  11. --
  12. --------------------------------------------------------------------------------
  13. ------------------------------------------------------------------------------*/
  14.  
  15. /* File Difference Utility */
  16.  
  17. #include <ctype.h>
  18. #include <stdio.h>
  19.  
  20. #define OPT_FLAG  '/'               /* command line option switch recognizer    */
  21.  
  22. #ifdef VAX11C
  23. #define MAXLINE 16                    /* maximum characters in input line    */
  24. #else
  25. #define MAXLINE 85
  26. #endif
  27.  
  28. #define FORMFEED 'L'-'@'
  29.  
  30. struct LINE {                   /* structure defining a line internally    */
  31.     int linenum;               /* what line on page            */
  32.     int pagenum;               /* what page line is from        */
  33.     struct LINE *link;           /* linked list pointer            */
  34.     char text[ MAXLINE ];        /* text of line                       */
  35.     char dup[ MAXLINE ];         /* uppercase copy of line text        */
  36. };
  37.  
  38. typedef struct LINE *line_ptr;
  39.  
  40. typedef char *char_ptr;
  41.  
  42. typedef FILE *FILE_PTR;
  43.  
  44. struct LINE root[ 3 ];            /* root of internal linked lists    */
  45.  
  46. FILE_PTR msg;                /* differences summary file pointer    */
  47.  
  48. int line_count[ 3 ] = { 1, 1, 1 };        /* file's line counter        */
  49. int page_count[ 3 ] = { 1, 1, 1 };        /* file's page counter        */
  50. int command_errors = 0;            /* how many command line errors    */
  51. char xx1[ 132 ], xx2[ 132 ];        /* space to retain file names    */
  52. int files = 0;                /* how many files specified on command line    */
  53. char_ptr infile_name[ 3 ] = { NULL, xx1, xx2 };
  54. char outfile_name[ 132 ];        /* changebarred output filename        */
  55. FILE_PTR infile[ 3 ];                      /* input file pointers        */
  56. FILE *outfile;                /* changebarred output file pointer    */
  57. static line_ptr at[ 3 ] = { NULL, &(root[ 1 ]), &(root[ 2 ]) };
  58.  
  59. int debug = 0;                      /* trace switch            */
  60. int trace_enabled = 0;              /* keyboard tracing switch        */
  61. int bar_col = 78;                  /* column where change bar is to appear    */
  62. int top_skip = 0;                /* lines to skip at top of page    */
  63. int bot_skip = 0;                /* lines to skip at bottom of page    */
  64. int page_len = 66;            /* length of a page        */
  65. int up_case = 0;            /* boolean, is upper/lower case significant?    */
  66. int re_sync = 5;        /* lines that must match for resynchronization    */
  67. int output = 0;            /* boolean, is change-barred output file on?    */
  68. int blanks = 0;            /* boolean, are blank lines significant?    */
  69. int lookahead = 200;    /* how many lines to look ahead before giving up      */
  70. int skip1 = 0;                 /* how many pages of first file to skip    */
  71. int skip2 = 0;            /* how many pages of second file to skip    */
  72.  
  73. #if 0 /* tracing and other debug functions turned off */
  74.  
  75. #define trace( x )        callstack( x )
  76. #define ret               { callpop(); return; }
  77. #define ret_val( x )      { callpop(); return( x ); }
  78. #define TRACER_FUNCTIONS
  79.  
  80. #else
  81.  
  82. #define trace( x )    /** nothing **/
  83. #define ret        { return; }
  84. #define ret_val( x )    { return( x ); }
  85.  
  86. #endif
  87.  
  88. /*------------------------------------------------------------------------------
  89. ------------------------------------------------------------------------------*/
  90. main( argc, argv )
  91.    int argc;
  92.    char *argv[];
  93. {
  94.    int i;
  95.     trace( "main" );
  96.     if( argc == 1 )
  97.         help();
  98.     msg = stdout;
  99.     for( i = 1; i < argc; i++ )
  100.         strip_opt( argv[ i ] );
  101.     if( files < 2 )
  102.     {
  103.         printf( "\nError: Must specify two files" );
  104.         exit( 2 );
  105.     }
  106.     open_files();
  107.     if( command_errors )
  108.         exit( 2 );
  109.     page_skip();
  110.     diff();
  111.     ret;
  112. }
  113.  
  114. /*------------------------------------------------------------------------------
  115. DONT_LOOK - Tells us whether or not this line should be considered for
  116. comparison or is a filler (e.g. header, blank) line.
  117. ------------------------------------------------------------------------------*/
  118. dont_look( line )
  119.    line_ptr line;
  120. {
  121.    int i;
  122.     trace( "dont_look" );
  123.     if( line == NULL )
  124.         ret_val( 0 );
  125.     if( line->linenum <= top_skip )
  126.         ret_val( 1 );
  127.     if( line->linenum > page_len - bot_skip )
  128.         ret_val( 1 );
  129.     if( !blanks )
  130.     {
  131.         for( i = 0; i < MAXLINE; i++ )
  132.             switch( line->text[ i ] )
  133.             {
  134.                 case '\0':
  135.                 case '\n':
  136.                     ret_val( 1 );
  137.                 case '\t':
  138.                 case ' ':
  139.                     break;
  140.                 default:
  141.                     ret_val( 0 );
  142.             }
  143.     }
  144.     ret_val( 0 );
  145. }
  146.  
  147. /*------------------------------------------------------------------------------
  148. EQUAL - tells us if the pointers 'a' and 'b' point to line buffers containing
  149. equivalent text or not.
  150. ------------------------------------------------------------------------------*/
  151. equal( a, b )
  152.    line_ptr a, b;
  153. {
  154.     trace( "equal" );
  155.     if( (a == NULL) || (b == NULL) )
  156.         ret_val( 0 );
  157.     if( up_case )
  158.         ret_val( !strcmp( a->dup, b->dup ) )
  159.     else
  160.         ret_val( !strcmp( a->text, b->text ) )
  161. }
  162.  
  163. /*------------------------------------------------------------------------------
  164. POSITION - moves the input pointer for file 'f' such that the next line to
  165. be read will be 'where'.
  166. ------------------------------------------------------------------------------*/
  167. position( f, where )
  168.    int f;
  169.    line_ptr where;
  170. {
  171.     trace( "position" );
  172.     at[ f ] = &root[ f ];
  173.     while( at[ f ]->link != where )
  174.         at[ f ] = at[ f ]->link;
  175.     ret;
  176. }
  177.  
  178. /*------------------------------------------------------------------------------
  179. FIX - fixes the end-of-line sequence on a VAX to be just a newline instead of
  180. a carriage-return/newline.
  181. ------------------------------------------------------------------------------*/
  182. char *fix( str )
  183.    char *str;
  184. {
  185.    char *strsave;
  186.  
  187.     trace( "fix" );
  188.     strsave = str;
  189.     if( str == NULL )
  190.         ret_val( NULL )
  191. #ifdef VAX11C
  192.     while( *str != '\0' )
  193.     {
  194.         if( match( str, "\r\n" ) )
  195.         {
  196.             *str = '\n';
  197.             *(str + 1) = '\0';
  198.         }
  199.         str++;
  200.     }
  201. #endif
  202.     ret_val( strsave );
  203. }
  204.  
  205. /*------------------------------------------------------------------------------
  206. INDEX - returns a pointer to the first occurance of 'c' in the string pointed
  207. to by 'str', or NULL if 'str' does not contain 'c'.
  208. ------------------------------------------------------------------------------*/
  209. char *index( str, c )
  210.    char *str, c;
  211. {
  212.     trace( "index" );
  213.     while( (*str != c) && *(str++) );
  214.     if( *str == c )
  215.             ret_val( str )
  216.     ret_val( NULL );
  217. }
  218.  
  219. /*------------------------------------------------------------------------------
  220. NEXT_LINE - allocates, links, and returns the next line from file 'f' if no
  221. lines are buffered, otherwise returns the next buffered line from file 'f'
  222. and updates the link pointer to the next buffered line.
  223. ------------------------------------------------------------------------------*/
  224. line_ptr next_line( f )
  225.    int f;
  226. {
  227.    char *malloc();
  228.    line_ptr temp, place_hold;
  229.  
  230.     trace( "next_line" );
  231.     if( at[ f ]->link != NULL )
  232.     {
  233.         at[ f ] = at[ f ]->link;
  234.         ret_val( at[ f ] );
  235.     }
  236.     else
  237.     {
  238.         at[ f ]->link = (line_ptr)malloc( sizeof( struct LINE ) );
  239.         if( at[ f ]->link == NULL )
  240.         {
  241.             printf( "\nOut of Memory" );
  242.             exit( 2 );
  243.         }
  244.         place_hold = at[ f ];
  245.         at[ f ] = at[ f ]->link;
  246.         at[ f ]->link = NULL;
  247.         if( fix( fgets( at[ f ]->text, MAXLINE, infile[ f ] ) ) == NULL)
  248.         {
  249.             free( at[ f ] );
  250.             at[ f ] = place_hold;
  251.             at[ f ]->link = NULL;
  252.             ret_val( NULL )
  253.         }
  254. #ifdef EMBEDDED_FORMFEEDS
  255.         if( (index( at[ f ]->text, FORMFEED ) != NULL) || 
  256.             (line_count[ f ] > page_len ) )
  257. #else
  258.         if( ( *(at[ f ]->text) == FORMFEED) || 
  259.             (line_count[ f ] > page_len ) )
  260. #endif
  261.         {
  262.             page_count[ f ]++;
  263.             line_count[ f ] = 1;
  264.         }
  265.         at[ f ]->linenum = line_count[ f ]++;
  266.         at[ f ]->pagenum = page_count[ f ];
  267.         if( up_case )
  268.         {
  269.             strcpy( at[ f ]->dup, at[ f ]->text );
  270.             upper( at[ f ]->dup );
  271.         }
  272.         ret_val( at[ f ] );
  273.     }
  274. }
  275.  
  276. /*------------------------------------------------------------------------------
  277. DISCARD - deallocates all buffered lines from the root up to and including
  278. 'to' for file 'f'.
  279. ------------------------------------------------------------------------------*/
  280. discard( f, to )
  281.    int f;
  282.    line_ptr to;
  283. {
  284.    line_ptr temp;
  285.  
  286.     trace( "discard" );
  287.     for(;;)
  288.     {
  289.         if( root[ f ].link == NULL )
  290.             break;
  291.         temp = root[ f ].link;
  292.         root[ f ].link = root[ f ].link->link;
  293.         free( temp );
  294.         if( temp == to )
  295.             break;
  296.     }
  297.     at[ f ] = &root[ f ];
  298.     ret;
  299. }
  300.  
  301. /*------------------------------------------------------------------------------
  302. VFPUTS - for VAX, un-fixes newline at end of line to be carriage-return/newline.
  303. ------------------------------------------------------------------------------*/
  304. vfputs( str, file )
  305.    char *str;
  306.    FILE *file;
  307. {
  308.    int i;
  309.     trace( "vfputs" );
  310. #ifdef VAX11C
  311.     for( i = 0; i < MAXLINE; i++ )
  312.     {
  313.         if( str[ i ] == '\n' )
  314.         {
  315.             strcpy( str + i, "\r\n" );
  316.             break;
  317.         }
  318.     }
  319.     fputs( str, file );
  320. #else
  321.     fputs( str, file );
  322. #endif
  323.     ret;
  324. }
  325.  
  326. /*------------------------------------------------------------------------------
  327. PUT - If change-barred output file is turned on, prints all lines from the
  328. root of file 1 up to and including 'line'. This is called only if a match
  329. exists for each significant line in file 2.
  330. ------------------------------------------------------------------------------*/
  331. put( line )
  332.    line_ptr line;
  333. {
  334.    line_ptr temp;
  335.  
  336.     trace( "put" );
  337.     if( output )
  338.         for( temp = root[ 1 ].link; ; )
  339.         {
  340.             if( temp == NULL )
  341.                 ret
  342.             vfputs( temp->text, outfile );
  343.             if( temp == line )
  344.                 ret
  345.             temp = temp->link;
  346.         }
  347.     ret;
  348. }
  349.  
  350. /*------------------------------------------------------------------------------
  351. CHANGE_BAR - inserts a change-bar into the text pointed to by
  352. 'str' and returns a pointer to 'str'.
  353. ------------------------------------------------------------------------------*/
  354. char *change_bar( str )
  355.    char *str;
  356. {
  357.    int i;
  358.    char temp[ MAXLINE + 1 ], *dest,*base;
  359.  
  360.     trace( "change_bar" );
  361.     base = str;
  362.     dest = temp;
  363.     i = 0;
  364.     if( bar_col != 0 )
  365.     {
  366.         for( i = 0; *str != '\n'; i++ )
  367.         {
  368.             if( (*str == '\r') && (*(str + 1) != '\n') )
  369.                 i = 0;
  370.             *(dest++) = *(str++);
  371.         }
  372.         while( i++ < bar_col ) 
  373.             *(str)++ = ' ';
  374.         strcpy( str, "|\n" );
  375.     }
  376.     else
  377.         if( str[ 0 ] != ' ' )
  378.         {
  379.             strcpy( temp, str );
  380.             strcpy( str + 1, temp );
  381.             str[ 0 ] = '|';
  382.         }
  383.     ret_val( base );
  384. }
  385.  
  386. /*------------------------------------------------------------------------------
  387. ADDED - Prints a change summary for all significant lines from the root of
  388. file 1 up to and including 'line'. If output is enabled, adds a change bar
  389. to the text and outputs the line to the output file.
  390. ------------------------------------------------------------------------------*/
  391. added( line )
  392.    line_ptr line;
  393. {
  394.    line_ptr temp;
  395.  
  396.     trace( "added" );
  397.     for( temp = root[ 1 ].link; ; )
  398.     {
  399.         if( temp == NULL )
  400.             ret
  401.         if( !dont_look( temp ) )
  402.             fprintf( msg, "+%d:%d -> %s", temp->pagenum, 
  403.                temp->linenum, temp->text );
  404.         if( output )
  405.             if( dont_look( temp ) )
  406.                 vfputs( temp->text, outfile );
  407.             else
  408.                 vfputs( change_bar( temp->text ), outfile );
  409.         if( temp == line )
  410.             ret
  411.         temp = temp->link;
  412.     }
  413. }
  414.  
  415. /*------------------------------------------------------------------------------
  416. DELETED - outputs a change summary for all lines in file 2 from the root up to
  417. and including 'line'.
  418. ------------------------------------------------------------------------------*/
  419. deleted( line )
  420.    line_ptr line;
  421. {
  422.    line_ptr temp;
  423.  
  424.     trace( "deleted" );
  425.     for( temp = root[ 2 ].link; ; )
  426.     {
  427.         if( temp == NULL )
  428.             ret
  429.         if( !dont_look( temp ) )
  430.             fprintf( msg, "-%d:%d -> %s", temp->pagenum,
  431.                temp->linenum, temp->text );
  432.         if( temp == line )
  433.             ret
  434.         temp = temp->link;
  435.     }
  436.     ret;
  437. }
  438.  
  439. /*------------------------------------------------------------------------------
  440. RESYNC - resynchronizes file 1 and file 2 after a difference is detected, and
  441. outputs changed lines and change summaries via added() and deleted(). Exits
  442. with the file inputs pointing at the next two lines that match, unless
  443. it is impossible to sync up again, in which case all lines in file 1 are
  444. printed via added(). Deallocates all lines printed by this function.
  445. ------------------------------------------------------------------------------*/
  446. resync( first, second )
  447.    line_ptr first, second;
  448. {
  449.    line_ptr file1_start, file2_start, last_bad1, last_bad2, t1, t2;
  450.    int i, j ,k, moved1, moved2;
  451.  
  452.     trace( "resync" );
  453.  
  454.     moved1 = 0;
  455.     file1_start = first;
  456.  
  457.     position( 1, first );
  458.     for( k = 0; k < lookahead; k++ )
  459.     {
  460.         while( dont_look( file1_start = next_line( 1 ) ) );
  461.         if( file1_start == NULL ) goto no_sy;
  462.  
  463.         moved2 = 0;
  464.         file2_start = second;
  465.  
  466.         position( 2, second );
  467.         for( j = 0; j < lookahead ; j++ )
  468.         {
  469.             while( dont_look( file2_start = next_line( 2 ) ) );
  470.             if( file2_start == NULL ) goto eof2;
  471.             
  472.             t1 = file1_start;
  473.             t2 = file2_start;
  474.             for( i = 0; (i < re_sync) && equal( t1, t2 ); i++ )
  475.             {
  476.                 while( dont_look( t1 = next_line( 1 ) ) );
  477.                 while( dont_look( t2 = next_line( 2 ) ) );
  478.                 if( (t1 == NULL) || (t2 == NULL) )
  479.                     break;
  480.             }
  481.             if( i == re_sync ) goto synced;
  482.  
  483.             last_bad2 = file2_start;
  484.             position( 2, file2_start );
  485.             while( dont_look( file2_start = next_line( 2 ) ) );
  486.             moved2 ++;
  487.         }
  488. eof2:
  489.         last_bad1 = file1_start;
  490.         position( 1, file1_start );
  491.         while( dont_look( file1_start = next_line( 1 ) ) );
  492.         moved1++;
  493.     }
  494.     printf( "\n*** ERROR - lost sync in file %s at page %d line %d",
  495.         infile_name[ 1 ], first->pagenum, first->linenum );
  496.     fclose( outfile );
  497.     exit( 2 );
  498. no_sy:
  499.     position( 1, first );
  500.     while( (first = next_line( 1 )) != NULL )
  501.     {
  502.         added( first );
  503.         discard( 1, first );
  504.     }    
  505.     ret;
  506. synced:
  507.     if( moved1 )
  508.     {
  509.         added( last_bad1 );
  510.         discard( 1, last_bad1 );
  511.     }
  512.     position( 1, file1_start );
  513.     if( moved2 )
  514.     {
  515.         deleted( last_bad2 );
  516.         discard( 2, last_bad2 );
  517.     }
  518.     position( 2, file2_start );
  519.     fprintf( msg, "\n" );
  520.     ret;
  521. }
  522.  
  523. /*------------------------------------------------------------------------------
  524. DIFF - differencing executive. Prints and deallocates all lines up to where
  525. a difference is detected, at which point resync() is called. Exits on end
  526. of file 1.
  527. ------------------------------------------------------------------------------*/
  528. diff()
  529. {
  530.    line_ptr first, second;
  531.  
  532.     trace( "diff" );
  533.     for( ;; )
  534.     {
  535.         while( dont_look( first = next_line( 1 ) ) );
  536.         if( first == NULL )
  537.         {
  538.             put( first );
  539.             ret;
  540.         }
  541.         while( dont_look( second = next_line( 2 ) ) );
  542.         if( equal( first, second ) )
  543.         {
  544.             put( first );
  545.             discard( 1, first );
  546.             discard( 2, second );
  547.         }
  548.         else
  549.             resync( first, second );
  550.         if( second == NULL )
  551.             ret
  552.     }
  553. }
  554.  
  555. /*------------------------------------------------------------------------------
  556. PAGE_SKIP - skips the first 'skip1' pages of file 1, and then the first 'skip2'
  557. pages of file 2. This is useful to jump over tables of contents, etc.
  558. ------------------------------------------------------------------------------*/
  559. page_skip()
  560. {
  561.    line_ptr first, second;
  562.  
  563.     trace( "page_skip" );
  564.     for( ; ; )
  565.     {
  566.         first = next_line( 1 );
  567.         if( (first == NULL) || (first->pagenum > skip1) )
  568.             break;
  569.         put( first );
  570.         discard( 1, first );
  571.     }
  572.     if( first != NULL )
  573.         position( 1, first );
  574.     for( ; ; )
  575.     {
  576.         second = next_line( 2 );
  577.         if( (second == NULL) || (second->pagenum > skip2) )
  578.             break;
  579.         discard( 2, second );
  580.     }
  581.     if( second != NULL )
  582.         position( 2, second );
  583.     ret;
  584. }
  585.  
  586. /*------------------------------------------------------------------------------
  587. HELP - outputs usage information.
  588. ------------------------------------------------------------------------------*/
  589. help()
  590. {
  591.     printf( "\nDIFF" );
  592.     printf( "\nText File Differencer and Change Barrer" );
  593.     printf( "\n" );
  594.     printf( "\nFormat:" );
  595.     printf( "\nDIFF [option{option}] newfile oldfile [barfile]" );
  596.     printf( "\n" );
  597.     printf( "\n   newfile = latest revision of text file" );
  598.     printf( "\n   oldfile = baseline to compare against" );
  599.     printf( "\n   barfile = output file if changebars are desired" );
  600.     printf( "\n" );
  601.     printf( "\nOptions:" );
  602. #ifdef TRACER_FUNCTIONS
  603.     printf( "\n   /TRACE       Makes a mess of the display and runs real 
  604.                                                                           slow" );
  605.     printf( "\n                default = trace off" );
  606.     printf( "\n" );
  607. #endif
  608.     printf( "\n   /BAR_COL=n   Column of output file in which change bar
  609.                            will appear" );
  610.     printf( "\n                default = 78" );
  611.     printf( "\n" );
  612.     printf( "\n   /TOP_SKIP=n  Lines at top of page to skip for running 
  613.                            heads & page nos." );
  614.     printf( "\n                default = 0" );
  615.     printf( "\n" );
  616.     printf( "\n   /BOT_SKIP=n  Lines at botom of page to skip for running 
  617.                           foots and page nos." );
  618.     printf( "\n                default = 0" );
  619.     printf( "\n" );
  620.     printf( "\n   /PAGE_LEN=n  Lines per page (embedded formfeeds over-
  621.                                   ride)" );
  622.     printf( "\n                default = 66" );
  623.     printf( "\n" );
  624.     printf( "\n   /UP_CASE     Upper/Lower case is significant/is not
  625.                            significant" );
  626.     printf( "\n   /NOUP_CASE   default" );
  627.     printf( "\n" );
  628.     printf( "\n   /RE_SYNC=n   Lines that must match before files are 
  629.                             considered synced" );
  630.     printf( "\n                after differences are found - default = 5" );
  631.     printf( "\n" );
  632.     printf( "\n   /OUTPUT=file File to redirect differences summary to. " );
  633.     printf( "\n                default = SYS$OUTPUT or console." );
  634.     printf( "\n" );
  635.     printf( "\n   /BLANKS      Blank lines are considered significant" );
  636.     printf( "\n   /NOBLANKS    default" );
  637.     printf( "\n" );
  638.     printf( "\n   /LOOKAHEAD=n Lines to look ahead in each file to resync 
  639.                              after difference" );
  640.     printf( "\n                default = 200" );
  641.     printf( "\n" );
  642.     printf( "\n   /SKIP1=n     Pages in NEWFILE to skip before compare.
  643.                              Also sets /SKIP2" );
  644.     printf( "\n                default = 0" );
  645.     printf( "\n" );
  646.     printf( "\n   /SKIP2=n     Pages in OLDFILE to skip before compare.
  647.                                Must be after /SKIP1" );
  648.     printf( "\n                default = 0" );
  649.     printf( "\n" );
  650. }
  651.  
  652. /*------------------------------------------------------------------------------
  653. OPEN_FILES - opens the input and output files.
  654. ------------------------------------------------------------------------------*/
  655. open_files()
  656. {
  657.    int i;
  658.  
  659.     trace( "open_files" );
  660.     for( i = 1; i < 3; i++ )
  661.         if( (infile[ i ] = fopen( infile_name[ i ], "r")) == NULL )
  662.         {
  663.             printf( "\nError: Can't open %s", infile_name[ i ] );
  664.             command_errors++;
  665.         }
  666.     if( files > 2 )
  667.         if( (outfile = fopen( outfile_name, "w" )) == NULL )
  668.         {
  669.             printf( "\nError: Can't create %s", outfile_name );
  670.             command_errors++;
  671.         }
  672.     ret;
  673. }
  674.  
  675. /*------------------------------------------------------------------------------
  676. REDIRECT - performs output redirection under VAX 11 VMS.
  677. ------------------------------------------------------------------------------*/
  678. redirect( str )
  679.    char *str;
  680. {
  681.    char filename[ 132 ], *ptr, *dest;
  682.  
  683.     trace( "redirect" );
  684.     dest = filename;
  685.     if( (ptr = index( str, '=' ) + 1) == (char *)(NULL + 1) )
  686.     {
  687.         printf( "\nERROR in option %s", str );
  688.         command_errors++;
  689.     }
  690.     while( (*ptr != OPT_FLAG) && ((*(dest++) = *(ptr++)) != '\0') );
  691.     *dest = '\0';
  692.     if( (msg = fopen( filename, "w" )) == NULL )
  693.     {
  694.         printf( "\nERROR creating %s", filename );
  695.         command_errors++;
  696.     }
  697.     ret;
  698. }    
  699.  
  700. /*------------------------------------------------------------------------------
  701. STRIP_OPT - processes each command line option.
  702. ------------------------------------------------------------------------------*/
  703. strip_opt( str )
  704.    char *str;
  705. {
  706.     trace( "strip_opt" );
  707.     upper( str );
  708.     if( str[ 0 ] == OPT_FLAG )
  709.     {
  710.         if( match( str + 1, "BAR_COL" ) )
  711.             bar_col = num( str );
  712.         else if( match( str + 1, "TOP_SKIP" ) )
  713.             top_skip = num( str );
  714.         else if( match( str + 1, "BOT_SKIP" ) )
  715.             bot_skip = num( str );
  716.         else if( match( str + 1, "PAGE_LEN" ) )
  717.             page_len = num( str );
  718.         else if( match( str + 1, "UP_CASE" ) )
  719.             up_case = 1;
  720.         else if( match( str + 1, "NOUP_CASE" ) )
  721.             up_case = 0;
  722.         else if( match( str + 1, "RE_SYNC" ) )
  723.             re_sync = num( str );
  724.         else if( match( str + 1, "BLANKS" ) )
  725.             blanks = 1;
  726.         else if( match( str + 1, "NOBLANKS" ) )
  727.             blanks = 0;
  728.         else if( match( str + 1, "LOOKAHEAD" ) )
  729.             lookahead = num( str );
  730.         else if( match( str + 1, "SKIP1" ) )
  731.             skip1 = skip2 = num( str );
  732.         else if( match( str + 1, "SKIP2" ) )
  733.             skip2 = num( str );
  734. #ifdef TRACER_FUNCTIONS
  735.         else if( match( str + 1, "TRACE" ) )
  736.             trace_enabled = debug = 1;
  737. #endif 
  738.         else if( match( str + 1, "OUTPUT" ) )
  739.             redirect( str );
  740.         else
  741.         {
  742.             printf( "\nUnrecognized Option: %s", str );
  743.             command_errors++;
  744.         }
  745.     }
  746.     else
  747.     {
  748.         switch( files ) 
  749.         {
  750.             case 0:
  751.                 strcpy( infile_name[ 1 ], str );
  752.                 break;
  753.             case 1:
  754.                 strcpy( infile_name[ 2 ], str );
  755.                 break;
  756.             case 2:
  757.                 strcpy( outfile_name, str );
  758.                 output = 1;
  759.                 break;
  760.             default:
  761.                 printf( "\nError: Too many files at %s", str );
  762.                 command_errors++;
  763.                 break;
  764.         }
  765.         files++;
  766.     }
  767.     if( index( str + 1, OPT_FLAG ) != NULL )
  768.         strip_opt( index( str + 1, OPT_FLAG ) );
  769.     ret;
  770. }
  771.  
  772. /*------------------------------------------------------------------------------
  773. UPPER - converts the string 'str' to upper case.
  774. ------------------------------------------------------------------------------*/
  775. upper( str )
  776.    char *str;
  777. {
  778.     trace( "upper" );
  779.     for( ; ; )
  780.     {
  781.         if( *str == '\0' )
  782.             ret
  783.         *str = toupper( *str );
  784.         str++;
  785.     }
  786. }
  787.  
  788. /*------------------------------------------------------------------------------
  789. MATCH - looks for a match of 'str' with the first (strlen( str) ) characters
  790. of 'pattern'. Returns 0 for no match, nonzero on match.
  791. ------------------------------------------------------------------------------*/
  792. int match( str, pattern )
  793.    char *str, *pattern;
  794. {
  795.     trace( "match" );
  796.     for( ; ; )
  797.     {
  798.         if( *str != *pattern )
  799.             ret_val( 0 )
  800.         str++;
  801.         pattern++;
  802.         if( *pattern == '\0' )
  803.             ret_val( 1 )
  804.         if( *str == '\0' )
  805.             ret_val( 1 )
  806.         if( *str == '=' )
  807.             ret_val( 1 )
  808.     }
  809. }
  810.  
  811. /*------------------------------------------------------------------------------
  812. NUM - returns the integer associated with a command line option. An equal
  813. sign must appear in the option.
  814. ------------------------------------------------------------------------------*/
  815. int num( str )
  816.    char *str;
  817. {
  818.     trace( "num" );
  819.     if( index( str, '=' ) == NULL )
  820.         ret_val( 0 )
  821.     else
  822.         ret_val( atoi( index( str, '=' ) + 1 ) )
  823. }
  824.  
  825. #ifdef TRACER_FUNCTIONS
  826.  
  827. char_ptr names[ 20 ];
  828. int stack = 0;
  829.  
  830. callstack( str )
  831.    char *str;
  832. {
  833.    int i;
  834.    char c;
  835.  
  836.     names[ stack++ ] = str;
  837.     if( debug )
  838.     {
  839.         for( i = 0; i < stack; i++ )
  840.             printf( "   " );
  841.         printf( "Entering %s\n", str );
  842.     }
  843. #ifndef VAX11C
  844.     if( trace_enabled && kbhit() )
  845.     {
  846.         switch( getch() )
  847.         {
  848.             case 't':
  849.             case 'T':
  850.                 debug = !debug;
  851.                 break;
  852.             case 's':
  853.             case 'S':
  854.                 printf( "\n-----------" );
  855.                 for( i = stack - 1; i >= 0; i-- )
  856.                     printf( "\n%s", names[ i ] );
  857.                 printf( "\n-----------\n" );
  858.                 break;
  859.             default:
  860.                 break;
  861.         }
  862.     }
  863. #endif
  864. }
  865.  
  866. callpop()
  867. {
  868.    int i;
  869.     if( debug )
  870.     {
  871.         for( i = 0; i < stack; i++ )
  872.             printf( "   " );
  873.         printf( "Exiting  %s\n", names[ stack ] );
  874.     }
  875.     stack--;
  876. }
  877.  
  878. #endif
  879.